package com.jboy.utils;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class SM4Encryption {
    public static void main(String[] args) throws Exception {
        // 明文和密钥
        String plaintext = "igcsdev";
        // 加密
        String encryptedHex = encrypt(plaintext);
        String encryptedHex1 = encrypt("igcs");
        System.out.println("加密结果: " + encryptedHex);
        System.out.println("加密结果: " + encryptedHex1);

        // 解密
        String decryptedData = decrypt(encryptedHex);
        System.out.println("解密结果: " + decryptedData);
    }

    /**
     * 秘钥key
     */
    private static final String _KEY = "bank.ccdc.com.cn";

    /**
     * 加密
     *
     * @param plaintext 明文
     * @return 密文
     * @throws Exception 异常
     */
    public static String encrypt(String plaintext) throws Exception {
        byte[] keyBytes = _KEY.getBytes(StandardCharsets.UTF_8);
        keyBytes = Arrays.copyOf(keyBytes, 16);
        byte[] encryptedData = encryptSM4(plaintext, keyBytes);

        return bytesToHex(encryptedData);
    }

    /**
     * 解密
     *
     * @param encryptedData 密文
     * @return 明文
     * @throws Exception 异常
     */
    public static String decrypt(String encryptedData) throws Exception {
        byte[] keyBytes = _KEY.getBytes(StandardCharsets.UTF_8);
        keyBytes = Arrays.copyOf(keyBytes, 16);
        byte[] decryptedData = decryptSM4(hexToBytes(encryptedData), keyBytes);

        return new String(decryptedData, StandardCharsets.UTF_8);
    }

    /**
     * SM4加密函数
     * @param plaintext 明文
     * @param keyBytes 密钥
     * @return 密文
     * @throws Exception 异常
     */
    private static byte[] encryptSM4(String plaintext, byte[] keyBytes) throws Exception {
        BlockCipher sm4Engine = new SM4Engine();
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(sm4Engine, new PKCS7Padding());
        cipher.init(true, new KeyParameter(keyBytes));

        byte[] input = plaintext.getBytes(StandardCharsets.UTF_8);
        byte[] encrypted = new byte[cipher.getOutputSize(input.length)];
        int len = cipher.processBytes(input, 0, input.length, encrypted, 0);
        len += cipher.doFinal(encrypted, len);  // 确保获取完整加密数据

        return Arrays.copyOf(encrypted, len);  // 返回有效的加密数据
    }

    /**
     * SM4解密函数
     * @param encryptedData 密文
     * @param keyBytes 密钥
     * @return 明文
     * @throws Exception 异常
     */
    private static byte[] decryptSM4(byte[] encryptedData, byte[] keyBytes) throws Exception {
        BlockCipher sm4Engine = new SM4Engine();
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(sm4Engine, new PKCS7Padding());
        cipher.init(false, new KeyParameter(keyBytes));

        byte[] decrypted = new byte[cipher.getOutputSize(encryptedData.length)];
        int len = cipher.processBytes(encryptedData, 0, encryptedData.length, decrypted, 0);
        len += cipher.doFinal(decrypted, len);  // 确保获取完整解密数据

        return Arrays.copyOf(decrypted, len);  // 返回有效的解密数据
    }

    /**
     * 将字节数组转换为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xFF & b);
            if (hex.length() == 1) {
                hexString.append('0');  // 补零，确保每个字节都是两位
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    /**
     * 将十六进制字符串转换为字节数组
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexToBytes(String hex) {

        if (hex.length() % 2 != 0) {
            throw new IllegalArgumentException("Invalid hexadecimal string");
        }

        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length(); i += 2) {
            bytes[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }

        return bytes;
    }
}
